package per.sunmes.lesrb;

import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.g2d.Batch;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.scenes.scene2d.Action;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.ui.Label;
import com.badlogic.gdx.utils.Align;
import com.badlogic.gdx.utils.viewport.ExtendViewport;
import com.badlogic.gdx.utils.viewport.Viewport;

import per.sunmes.lesrb.apis.Logger;
import per.sunmes.lesrb.listener.TimeUpdateListener;
import per.sunmes.lesrb.manager.LayerM;
import per.sunmes.lesrb.manager.RM;
import per.sunmes.lesrb.manager.SM;
import per.sunmes.lesrb.util.GM;

public abstract class ExGameMain implements ApplicationListener {

	/**
	 * value set until create() called.</br>
	 * value will set to null when dispose() called.
	 */
	public static ExGameMain I;
	protected Stage stage;
	protected Viewport viewport;
	protected Batch batch;
	protected boolean isBatchOwnDispose;
	private long timeTmp;

	@Override
	public void create() {
		I = this;
		Gdx.gl.glClearColor(0, 0, 0, 1);

		Gdx.input.setCatchBackKey(true);

		initialzeValueSet();
		managersInit();
		stageInit();

		stageStart();
	}

	public static final String DEBUG_LABEL_NAME = "DebugLabel";

	/**
	 * add label to Stage, and addUpdateDebugInfo action.
	 * 
	 * @param debugLabel
	 * @param updateDelay
	 *            updateDelay
	 */
	public void setLabelToDebugLabel(final Label debugLabel, final float updateDelay) {
		stage.addActor(debugLabel);
		debugLabel.setName(DEBUG_LABEL_NAME);
		debugLabel.setAlignment(Align.bottomLeft);
		debugLabel.setPosition(10, 10);
		debugLabel.addAction(new Action() {
			StringBuilder sb = new StringBuilder();
			float actUseTime, drawUseTime;
			int useTimeCount;
			float dtUpdate = updateDelay;

			@Override
			public boolean act(float delta) {
				dtUpdate += delta;
				actUseTime += GM.stageActUseTime;
				drawUseTime += GM.stageDrawUseTime;
				useTimeCount++;
				if (dtUpdate >= updateDelay) {
					dtUpdate = 0;
					sb.delete(0, sb.length());
					sb.append("FPS:").append(Gdx.graphics.getFramesPerSecond())//
							.append("\nActUseTime:").append(actUseTime / 1000000f / useTimeCount).append(" ms")//
							.append("\nDrawUseTime:").append(drawUseTime / 1000000f / useTimeCount).append(" ms")//
							.append("\nUseMemory:").append(Gdx.app.getJavaHeap() / 1000).append("KB")//
							.append("\nFreeMemory:").append(Runtime.getRuntime().freeMemory() / 1000).append("KB")//
							.append("\nTotalMemory:").append(Runtime.getRuntime().totalMemory() / 1000).append("KB")//
					;
					debugLabel.setText(sb);

					actUseTime = 0;
					drawUseTime = 0;
					useTimeCount = 0;
				}
				return false;
			}
		});
	}

	protected void stageInit() {
		if (GM.gamePixelWidth < 1) {
			GM.gamePixelWidth = 1280;
		}
		if (GM.gamePixelHeight < 1) {
			GM.gamePixelHeight = 720;
		}
		if (viewport == null) {
			viewport = new ExtendViewport(GM.gamePixelWidth, GM.gamePixelHeight);
		}
		if (batch == null) {
			batch = new SpriteBatch();
		}
		isBatchOwnDispose = true;

		if (stage == null) {
			stage = new Stage(viewport, batch);
		}
		Gdx.input.setInputProcessor(stage);

		stage.addActor(LayerM.layerMActor());
		stage.addListener(LayerM.layerMInputListener());
		///
		GM.stage = stage;
	}

	protected void managersInit() {
		if (GM.logger == null) {
			GM.logger = new Logger() {

				@Override
				public void log(String tag, String message) {
					System.out.println(message);
				}

				@Override
				public void log(String tag, String message, Throwable exception) {
					System.out.println(message);
				}

				@Override
				public void error(String tag, String message) {
					System.out.println(message);
				}

				@Override
				public void error(String tag, String message, Throwable exception) {
					System.out.println(message);
				}

				@Override
				public void debug(String tag, String message) {
					System.out.println(message);
				}

				@Override
				public void debug(String tag, String message, Throwable exception) {
					System.out.println(message);
				}

			};
		}

		RM.getAsset();
		SM.getCurrentMusicPathName();
		LayerM.layerMActor();
	}

	protected void managersDispose() {
		RM.dispose();
		SM.dispose();
		LayerM.dispose();
	}

	/**
	 * set Managers initialze values </br>
	 * eg.
	 * 
	 * <pre>
	 * GM.gamePixelWidth=1280; 
	 * GM.gamePixelHeight=720;
	 * 
	 * batch=..
	 * viewport=..
	 * 
	 * RM.RES_DIR=..
	 * </pre>
	 */
	public abstract void initialzeValueSet();

	/** game start logic after initialzed. */
	public abstract void stageStart();

	@Override
	public void dispose() {
		childDispose();
		if (batch != null) {
			if (isBatchOwnDispose) {
				batch.dispose();
				batch = null;
			}
		}
		if (stage != null) {
			stage.dispose();
			stage = null;
		}
		/////////
		managersDispose();
		////////
		I = null;
	}

	public abstract void childDispose();

	@Override
	public void pause() {
	}

	float delayTime;

	@Override
	public void render() {
		if (GM.stage == null) {
			GM.stage = stage;
		}
		Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
		timeTmp = System.nanoTime();

		delayTime = Gdx.graphics.getDeltaTime();
		//////
		GMAction(delayTime);
		/////////

		stage.act(delayTime);
		GM.stageActUseTime = System.nanoTime() - timeTmp;
		stage.draw();
		GM.stageDrawUseTime = System.nanoTime() - timeTmp - GM.stageActUseTime;
	}

	private void GMAction(float delta) {
		if (GM.timeUpdateListeners.size > 0) {
			for (TimeUpdateListener listener : GM.timeUpdateListeners) {
				try {
					listener.update(delta);
				} catch (Exception e) {
				}
			}
		}

		//////
		if (GM.actionActors.size > 0) {
			for (int i = 0; i < GM.actionActors.size; i++) {
				GM.actionActors.get(i).act(delta);
			}
			for (int i = 0; i < GM.actionActors.size; i++) {
				if (!GM.actionActors.get(i).hasActions()) {
					GM.actionActors.removeIndex(i);
					i--;
				}
			}
		}
	}

	@Override
	public void resize(int width, int height) {
		viewport.update(width, height);
	}

	@Override
	public void resume() {
	}

}
